/**********************************************************************/
/*                  Symbol File class for J2CPS                       */
/*                                                                    */   
/*                      (c) copyright QUT                             */ 
/**********************************************************************/
package J2CPS;

import java.io.*;
import java.util.ArrayList;

class SymbolFile {
 
/************************************************************************/
/*             Symbol file reading/writing                              */
/************************************************************************/
  // Collected syntax ---
  // 
  // SymFile    = Header [String (falSy | truSy)]
  //		{Import | Constant | Variable | Type | Procedure} 
  //		TypeList Key.
  // Header     = magic modSy Name.
  // Import     = impSy Name [String] Key.
  // Constant   = conSy Name Literal.
  // Variable   = varSy Name TypeOrd.
  // Type       = typSy Name TypeOrd.
  // Procedure  = prcSy Name [String] [truSy] FormalType.
  // Method     = mthSy Name Byte Byte TypeOrd [String] FormalType.
  // FormalType = [retSy TypeOrd] frmSy {parSy Byte TypeOrd} endFm.
  // TypeOrd    = ordinal.
  // TypeHeader = tDefS Ord [fromS Ord Name].
  // TypeList   = start {Array | Record | Pointer | ProcType | 
  //                     NamedType | Enum} close.
  // Array      = TypeHeader arrSy TypeOrd (Byte | Number | ) endAr.
  // Pointer    = TypeHeader ptrSy TypeOrd.
  // ProcType   = TypeHeader pTpSy FormalType.
  // EventType  = TypeHeader evtSy FormalType.
  // Record     = TypeHeader recSy recAtt [truSy | falSy] [basSy TypeOrd] 
  //              [iFcSy basSy TypeOrd {basSy TypeOrd}]
  //              {Name TypeOrd} {Method} {Statics} endRc.
  // Statics    = ( Constant | Variable | Procedure ).
  // Enum       = TypeHeader eTpSy { Constant } endRc.
  // NamedType  = TypeHeader. 
  // Name       = namSy byte UTFstring.
  // Literal    = Number | String | Set | Char | Real | falSy | truSy.
  // Byte       = bytSy byte.
  // String     = strSy UTFstring.
  // Number     = numSy java.lang.long.
  // Real       = fltSy java.lang.double.
  // Set        = setSy java.lang.int.
  // Key        = keySy java.lang.int.
  // Char       = chrSy java.lang.char.
  //
  // Notes on the syntax:
  // All record types must have a Name field, even though this is often
  // redundant.  The issue is that every record type (including those that
  // are anonymous in CP) corresponds to a Java class, and the definer 
  // and the user of the class _must_ agree on the JVM name of the class.
  // The same reasoning applies to procedure types, which must have equal
  // interface names in all modules.
  //

  static final String[] mthAtt = {"", ",NEW", ",ABSTRACT", ",NEW,ABSTRACT",
                                  ",EMPTY", ",NEW,EMPTY",
                                  ",EXTENSIBLE", ",NEW,EXTENSIBLE"};
  static final String[] recAtt = {"RECORD ", "ABSTRACT RECORD ",
                                  "LIMITED RECORD ", "EXTENSIBLE RECORD "};
  static final String[] mark = {"", "*", "-", "!"};
  static final String[] varMark = {"", "IN", "OUT", "VAR"};

  private static final String spaces = "         ";
  private static final String recEndSpace = "      "; 
  private static final char qSepCh = '/';

  static final int modSy = (int) 'H';
  static final int namSy = (int) '$';
  static final int bytSy = (int) '\\';
  static final int numSy = (int) '#';
  static final int chrSy = (int) 'c';
  static final int strSy = (int) 's';
  static final int fltSy = (int) 'r';
  static final int falSy = (int) '0';
  static final int truSy = (int) '1';
  static final int impSy = (int) 'I';
  static final int setSy = (int) 'S';
  static final int keySy = (int) 'K';
  static final int conSy = (int) 'C';
  static final int typSy = (int) 'T';
  static final int tDefS = (int) 't';
  static final int prcSy = (int) 'P';
  static final int retSy = (int) 'R';
  static final int mthSy = (int) 'M';
  static final int varSy = (int) 'V';
  static final int parSy = (int) 'p';
  static final int iFcSy = (int) '~';
  
  static final int start = (int) '&';
  static final int close = (int) '!';
  
  static final int recSy = (int) '{';
  static final int endRc = (int) '}';
  static final int frmSy = (int) '(';
  static final int fromS = (int) '@';
  static final int endFm = (int) ')';
  static final int arrSy = (int) '[';
  static final int endAr = (int) ']';
  static final int pTpSy = (int) '%';
  static final int evtSy = (int) 'v';
  static final int ptrSy = (int) '^';
  static final int basSy = (int) '+';
  static final int eTpSy = (int) 'e';
  
  static final int magic = 0xdeadd0d0;

  static final int prvMode = 0;
  static final int pubMode = 1;
  static final int rdoMode = 2;
  static final int protect = 3;

  private static final int initTypeListSize = 128;
  public static TypeDesc[] typeList = new TypeDesc[initTypeListSize];
  private static int nextType = TypeDesc.ordT;
  private static int tListIx = 0;
  private static int sSym = 0;
  private static int acc = 0;
  private static String name;
  private static int iVal;
  private static long lVal;
  private static int tOrd;
  private static char cVal;
  private static double dVal;
  private static DataInputStream in;

// Symbol file writing 

  static void writeName(DataOutputStream out,int access, String name) 
                                                            throws IOException{
    out.writeByte(namSy);
    if (ConstantPool.isPublic(access))   { out.writeByte(pubMode); }
    else if (ConstantPool.isProtected(access)) { out.writeByte(protect); }
    else /* if (ConstantPool.isPrivate(access)) */ { out.writeByte(prvMode); }
    out.writeUTF(name);
  }

  static void writeString(DataOutputStream out,String str) throws IOException {
    out.writeByte(strSy);
    out.writeUTF(str);
  }

  static void writeLiteral(DataOutputStream out,Object val) throws IOException {
    if (val instanceof String) {
      writeString(out,(String) val);
    } else if (val instanceof Integer) {
      out.writeByte(numSy);
      out.writeLong(((Integer)val).longValue());
    } else if (val instanceof Long) {
      out.writeByte(numSy);
      out.writeLong(((Long)val).longValue());
    } else if (val instanceof Float) {
      out.writeByte(fltSy);
      out.writeDouble(((Float)val).doubleValue());
    } else if (val instanceof Double) {
      out.writeByte(fltSy);
      out.writeDouble(((Double)val).doubleValue());
    } else {
      System.out.println("Unknown constant type");
      System.exit(1);
    }
  }

  public static void writeOrd(DataOutputStream out,int i) throws IOException {
    // DIAGNOSTIC
    if (i < 0)
      throw new IOException(); 
    // DIAGNOSTIC
    if (i <= 0x7f) {
      out.writeByte(i);
    } else if (i <= 0x7fff) {
      out.writeByte(128 + i % 128);  
      out.writeByte(i / 128);
    } else {
      throw new IOException(); 
    }
  }

  private static void InsertType(TypeDesc ty) {
    if (ty.outTypeNum > 0) { return; }
    ty.outTypeNum = nextType++;
    if (tListIx >= typeList.length) {
      TypeDesc[] tmp = new TypeDesc[typeList.length + initTypeListSize];
      System.arraycopy(typeList, 0, tmp, 0, typeList.length);
      typeList = tmp;
    }
    typeList[tListIx++] = ty;
  }

  public static void AddType(TypeDesc ty) {
    InsertType(ty); 
    if (!ty.writeDetails) { return; }
    if (ty instanceof ClassDesc) {
      ClassDesc aClass = (ClassDesc)ty;
      if (aClass.outBaseTypeNum > 0) { return; }
      aClass.outBaseTypeNum = nextType++;
      if (aClass.superClass != null) {
        aClass.superClass.writeDetails = true; 
        AddType(aClass.superClass); 
      }
      if (aClass.isInterface) {
        for (int i=0; i < aClass.interfaces.length; i++) {
          aClass.interfaces[i].writeDetails = true;
          AddType(aClass.interfaces[i]);
        }
      }
    } else if (ty instanceof PtrDesc) { 
        ty = ((PtrDesc)ty).boundType; 
        if (ty.outTypeNum == 0) { AddType(ty); }
    } else if (ty instanceof ArrayDesc) {
      ty = ((ArrayDesc)ty).elemType;
      while (ty instanceof ArrayDesc) {
        ArrayDesc aTy = (ArrayDesc)ty;
        if (aTy.ptrType.outTypeNum == 0) { InsertType(aTy.ptrType); }
        if (aTy.outTypeNum == 0) { InsertType(aTy); }
        ty = aTy.elemType;
      }                   
      if (ty.outTypeNum == 0) { InsertType(ty); }
    }
  }

  static void writeTypeOrd(DataOutputStream out,TypeDesc ty)throws IOException {
    if (ty.typeOrd < TypeDesc.ordT) { 
      out.writeByte(ty.typeOrd); 
    } else {
      if (ty.outTypeNum == 0) { AddType(ty); }
      if (ty.outTypeNum == 0) { 
        System.out.println("ERROR: type has number 0 for type " + ty.name); 
        System.exit(1); 
      }
      writeOrd(out,ty.outTypeNum);
    }
  }

  public static void WriteFormalType(MethodInfo m,DataOutputStream out) 
                                                     throws IOException {
    if ((m.retType != null) && (m.retType.typeOrd != 0)) {
      out.writeByte(retSy);
      writeTypeOrd(out,m.retType);
    } 
    out.writeByte(frmSy);
    for (int i=0; i < m.parTypes.length; i++) {
      out.writeByte(parSy);
      if (m.parTypes[i] instanceof ArrayDesc) {
        out.writeByte(1);   // array params are IN
      } else {
        out.writeByte(0);   // all other java parameters are value 
      }
      writeTypeOrd(out,m.parTypes[i]);
    }
    out.writeByte(endFm);
  }

  public static void WriteSymbolFile(PackageDesc thisPack) throws IOException{
    ClearTypeList();
    DataOutputStream out = J2CPSFiles.CreateSymFile(thisPack.cpName);

    System.out.println("INFO:  Creating symbol file " + thisPack.cpName);

    out.writeInt(magic);
    out.writeByte(modSy);
    writeName(out,0,thisPack.cpName);
    writeString(out,thisPack.javaName);
    out.writeByte(falSy); /* package is not an interface */
    for (int i=0; i < thisPack.imports.size(); i++) {
      out.writeByte(impSy);
      PackageDesc imp = (PackageDesc)thisPack.imports.get(i);
      imp.impNum = i+1;
      writeName(out,0,imp.cpName);
      writeString(out,imp.javaName);
      out.writeByte(keySy);
      out.writeInt(0);
    }
    for (int cNum=0; cNum < thisPack.classes.length; cNum++) {
      ClassDesc thisClass = thisPack.classes[cNum];
      if ((thisClass != null) && ConstantPool.isPublic(thisClass.access)) {
        thisClass.writeDetails = true;
        out.writeByte(typSy);
        writeName(out,thisClass.access,thisClass.objName);
        writeTypeOrd(out,thisClass);
      }
    }
    out.writeByte(start);
    for (int i=0; i < tListIx; i++) {
      out.writeByte(tDefS);
      writeOrd(out,typeList[i].outTypeNum);
      typeList[i].writeType(out,thisPack);
    }
    out.writeByte(close);
    out.writeByte(keySy);
    out.writeInt(0);
    thisPack.ResetImports();
  }

// Symbol file reading 

  private static void InsertType(int tNum,TypeDesc ty) {
    if (tNum >= typeList.length) {
      int newLen = 2 * typeList.length;
      while (tNum >= newLen) { newLen += typeList.length; }
      TypeDesc[] tmp = new TypeDesc[newLen];
      System.arraycopy(typeList, 0, tmp, 0, typeList.length);
      typeList = tmp;
    }
    typeList[tNum] = ty;
  }


  private static int readOrd() throws IOException {
    int b1 = in.readUnsignedByte();
    if (b1 <= 0x7f) { return b1; }
    else { int b2 = in.readByte();
           return b1 - 128 + b2 * 128; }
  }

  private static void GetSym() throws IOException {
    sSym = in.readByte();
    switch (sSym) {
      case namSy : acc = in.readByte();         // fall through 
      case strSy : name = in.readUTF(); break;
      case arrSy :
      case ptrSy :
      case retSy : 
      case fromS : 
      case tDefS : 
      case basSy : tOrd = readOrd(); break;
      case bytSy : iVal = in.readByte(); break;
      case keySy : 
      case setSy : iVal = in.readInt(); break;
      case numSy : lVal = in.readLong(); break;
      case fltSy : dVal = in.readDouble(); break;
      case chrSy : cVal = in.readChar(); break;
      case modSy : 
      case impSy : 
      case conSy : 
      case varSy : 
      case typSy : 
      case prcSy : 
      case mthSy : 
      case parSy : 
      case start : 
      case close : 
      case falSy : 
      case truSy : 
      case frmSy : 
      case endFm : 
      case recSy : 
      case endRc : 
      case endAr : 
      case eTpSy :
      case iFcSy :
      case evtSy :
      case pTpSy : break;
      default:  char ch = (char) sSym;
                System.out.println("Bad symbol file format." +ch+"  "+sSym);
                System.exit(1);
    }
  }

  private static void Expect(int expSym) throws IOException {
    if (expSym != sSym) {
      System.out.println("Error in symbol file:  expecting " + 
      String.valueOf((char) expSym) + " got " +
      String.valueOf((char) sSym));
      System.exit(1);
    }
    GetSym();
  }

  private static void Check(int expSym) {
    if (expSym != sSym) {
      System.out.println("Error in symbol file:  checking " + 
      String.valueOf((char) expSym) + " got " +
      String.valueOf((char) sSym));
      System.exit(1);
    }
  }

  private static void SkipToEndRec(DataInputStream in) throws IOException {
    while (sSym != endRc) { 
      if (sSym == mthSy) {
        GetSym(); // name
        in.readByte(); 
        in.readByte();
        readOrd();
      } else if (sSym == varSy) {
        GetSym(); // name
        readOrd();
      } else if (sSym == conSy) {
        GetSym(); // name
        GetSym(); // Literal
      } else if (sSym == prcSy) {
        GetSym(); // name
      } else if (sSym == parSy) {
        in.readByte();
        readOrd();
      } else if (sSym == namSy) {
        readOrd();
      } else {
      }
      GetSym(); 
    }
  }

  private static int GetAccess() {
    if (acc == prvMode) { return ConstantPool.ACC_PRIVATE; }
    else if (acc == pubMode) { return ConstantPool.ACC_PUBLIC; }
    else if (acc == protect) { return ConstantPool.ACC_PROTECTED; }
    return 0;
  }

  private static ClassDesc GetClassDesc(PackageDesc thisPack,String className) {
    ClassDesc aClass = ClassDesc.GetClassDesc(thisPack.name + qSepCh + 
                                              className,thisPack);
    if (aClass.fieldList == null){ aClass.fieldList = new ArrayList(); }
    if (aClass.methodList == null){ aClass.methodList = new ArrayList(); }
    return aClass;
  }

  private static void GetConstant(ClassDesc cClass) throws IOException {
  // Constant = conSy Name Literal.
  // Literal  = Number | String | Set | Char | Real | falSy | truSy.
    TypeDesc typ = null;
    Object val = null;
    Expect(conSy);
    String constName = name; 
    int fAcc = GetAccess();
    fAcc = fAcc + ConstantPool.ACC_STATIC + ConstantPool.ACC_FINAL;
    Expect(namSy); 
    switch (sSym) {
      case numSy : typ = TypeDesc.GetBasicType(TypeDesc.longT); 
                   val = new Long(lVal); break;
      case strSy : typ = TypeDesc.GetBasicType(TypeDesc.strT);
                   val = name; 
      case setSy : typ = TypeDesc.GetBasicType(TypeDesc.setT);
                   val = new Integer(iVal); break;
      case chrSy : typ = TypeDesc.GetBasicType(TypeDesc.charT);
                   val = new Character(cVal); break;
      case fltSy : typ = TypeDesc.GetBasicType(TypeDesc.dbleT);
                   val = new Double(dVal); break;
      case falSy : typ = TypeDesc.GetBasicType(TypeDesc.boolT);
                   val = false; break;
      case truSy : typ = TypeDesc.GetBasicType(TypeDesc.boolT);
                   val = true; break;
    }
    boolean ok = cClass.fieldList.add(new FieldInfo(cClass,fAcc,constName,typ,val));
    GetSym();
  }

  private static void GetVar(ClassDesc vClass) throws IOException {
  // Variable = varSy Name TypeOrd.
    Expect(varSy);
    String varName = name; 
    int fAcc = GetAccess();
    Check(namSy);
    FieldInfo f = new FieldInfo(vClass,fAcc,varName,null,null);
    f.typeFixUp = readOrd();
    vClass.fieldList.add(f);
    GetSym();
  }

  private static void GetType(PackageDesc thisPack) throws IOException {
  // Type = typSy Name TypeOrd.
    Expect(typSy);
    ClassDesc thisClass = GetClassDesc(thisPack,name); 
    thisClass.access = GetAccess();
    Check(namSy);
    int tNum = readOrd();
    thisClass.inTypeNum = tNum;
    InsertType(tNum,thisClass);
    GetSym();
  }

  private static void GetFormalType(ClassDesc thisClass,MethodInfo thisMethod) 
                                                           throws IOException {
  // FormalType = [retSy TypeOrd] frmSy {parSy Byte TypeOrd} endFm.
    int [] pars = new int[20];
    int numPars = 0;
    TypeDesc retType = TypeDesc.GetBasicType(TypeDesc.noTyp);
    if (sSym == retSy) { thisMethod.retTypeFixUp = tOrd; GetSym();} 
    Expect(frmSy);
    while (sSym != endFm) {
      Check(parSy);
      in.readByte();   /* ignore par mode */
      pars[numPars++] = readOrd(); 
      GetSym();
    }  
    Expect(endFm);
    thisMethod.parFixUps = new int[numPars];
    System.arraycopy(pars, 0, thisMethod.parFixUps, 0, numPars);
  }


  private static void GetMethod(ClassDesc thisClass) throws IOException {
  // Method = mthSy Name Byte Byte TypeOrd [String] FormalType.
    String jName = null;
    Expect(mthSy);
    Check(namSy);
    String nam = name; 
    int pAcc = GetAccess();
    int attr = in.readByte();
    int recMode = in.readByte();  
    int cNum = readOrd();
    if (cNum != thisClass.inTypeNum) {
      System.err.println("Method not part of THIS class!");
      System.exit(1);
    }  
    GetSym();
    if (sSym == strSy) { jName = name; GetSym();  }
    MethodInfo m = new MethodInfo(thisClass,nam,jName,pAcc); 
    switch (attr) {
      case 1 : if (!m.isInitProc) {
                 m.accessFlags += ConstantPool.ACC_FINAL; 
               }
               break;
      case 2 : m.overridding = true;
               m.accessFlags += (ConstantPool.ACC_ABSTRACT + 
                                 ConstantPool.ACC_FINAL); 
               break;
      case 3 : m.accessFlags += (ConstantPool.ACC_ABSTRACT +
                                 ConstantPool.ACC_FINAL); 
               break;
      case 6 : m.overridding = true;
               break;
      case 7 : break; 
    }
    GetFormalType(thisClass,m);
    thisClass.methodList.add(m);
    thisClass.scope.put(m.name,m);
  }

  private static void GetProc(ClassDesc pClass) throws IOException {
  // Proc = prcSy Name [String] [truSy] FormalType.
    String jName = null;
    Expect(prcSy);
    String procName = name; 
    int pAcc = GetAccess();
    pAcc = pAcc + ConstantPool.ACC_STATIC;
    Expect(namSy); 
    if (sSym == strSy) { jName = name; GetSym();  }
    MethodInfo m = new MethodInfo(pClass,procName,jName,pAcc); 
    if (sSym == truSy) { m.isInitProc = true; GetSym();  }
    GetFormalType(pClass,m);
    pClass.methodList.add(m);
  }

  private static void ClearTypeList() {
    for (int i=0; i < typeList.length; i++) {
      if (typeList[i] != null) {
        if (typeList[i].typeOrd >= TypeDesc.specT) {
          typeList[i].inTypeNum = 0; 
          typeList[i].outTypeNum = 0; 
        }
        if (typeList[i] instanceof ClassDesc) {
          ((ClassDesc)typeList[i]).inBaseTypeNum = 0;
          ((ClassDesc)typeList[i]).outBaseTypeNum = 0;
          ((ClassDesc)typeList[i]).writeDetails = false;
        } else if (typeList[i] instanceof ArrayDesc) {
          ((ArrayDesc)typeList[i]).elemTypeFixUp = 0;
        }
      }
      typeList[i] = null; 
    }
    tListIx = 0;
    nextType = TypeDesc.ordT;
  }

  private static void FixArrayElemType(ArrayDesc arr) {
    if (arr.elemTypeFixUp == 0) { return; }
    TypeDesc elem = GetFixUpType(arr.elemTypeFixUp);
    if (elem instanceof ArrayDesc) {
      FixArrayElemType((ArrayDesc)elem); 
      arr.dim = ((ArrayDesc)elem).dim + 1;
      arr.ultimateElemType = ((ArrayDesc)elem).ultimateElemType;
    } else {
      arr.ultimateElemType = elem;
    }
    arr.elemType = elem;
  }

  private static TypeDesc GetFixUpType (int num) {
    if (num < TypeDesc.specT) { return TypeDesc.GetBasicType(num); }
    if (typeList[num] instanceof PtrDesc) { 
      return ((PtrDesc)typeList[num]).boundType;
    }
    return typeList[num];
  } 

  public static void ReadSymbolFile(File symFile,PackageDesc thisPack) 
                                     throws FileNotFoundException, IOException {

    if (ClassDesc.verbose)
      System.out.println("INFO:  Reading symbol file " + symFile.getName());

    ClearTypeList();
    ClassDesc aClass, impClass;
    int maxInNum = 0;
    FileInputStream fIn = new FileInputStream(symFile);
    in = new DataInputStream(fIn);
    if (in.readInt() != magic) {
      System.out.println(symFile.getName() + " is not a valid symbol file.");
      System.exit(1);
    }
    GetSym();
    Expect(modSy);
    if (!thisPack.cpName.equals(name)) {
      System.out.println("ERROR:  Symbol file " + symFile.getName() + 
      " does not contain MODULE " + thisPack.cpName + ", it contains MODULE " +
      name);
      System.exit(1);
    }
    Expect(namSy);
    if (sSym == strSy) { 
      if (!name.equals(thisPack.javaName)) { 
        System.out.println("Wrong name in symbol file.");
        System.exit(1); 
      }
      GetSym();
      if (sSym == truSy) {  
        System.out.println("ERROR:  Java Package cannot be an interface.");
        System.exit(1);
      }
      GetSym();
    } else {
      System.err.println("<" + symFile.getName() + 
                         "> NOT A SYMBOL FILE FOR A JAVA PACKAGE!");
      System.exit(1);
    }
    while (sSym != start) {
      switch (sSym) {
        case impSy : GetSym(); // name
                     String iName = name;
                     GetSym(); 
                     if (sSym == strSy) { 
                       PackageDesc pack = PackageDesc.getPackage(name);
                       thisPack.imports.add(pack);
                       GetSym(); 
                     }
                     Expect(keySy);  
                     break;
        case conSy : 
        case varSy : 
        case prcSy : System.out.println("Symbol File is not from a java class");
                     System.exit(1);
                     break;
        case typSy : GetType(thisPack); break;
      }
    }
    Expect(start);
    while (sSym != close) {
      PackageDesc impPack;
      impClass = null;
      String impModName = null;
      int impAcc = 0, impModAcc = 0;
      Check(tDefS); 
      int tNum = tOrd; GetSym(); 
      if (tNum > maxInNum) { maxInNum = tNum; }
      if (sSym == fromS) {
        int impNum = tOrd - 1;
        GetSym(); 
        Check(namSy);
        String impName = name;
        impAcc = acc; 
        if (impNum < 0) {
          impPack = thisPack;
        } else {
          impPack = (PackageDesc)thisPack.imports.get(impNum);
        }
        impClass = GetClassDesc(impPack,impName);
        GetSym(); 
      }
      switch (sSym) { 
        case arrSy : ArrayDesc newArr = null;
                     int elemOrd = tOrd;
                     GetSym();
                     Expect(endAr);
                     TypeDesc eTy = null;
                     if (elemOrd < typeList.length) {
                       if (elemOrd < TypeDesc.specT) { 
                         eTy = TypeDesc.GetBasicType(elemOrd); 
                       } else { 
                         eTy = typeList[elemOrd]; 
                       }
                       if ((eTy != null) && (eTy instanceof PtrDesc) &&
                          (((PtrDesc)eTy).boundType != null) && 
                          (((PtrDesc)eTy).boundType instanceof ClassDesc)) {
                         eTy = ((PtrDesc)eTy).boundType;   
                       } 
                     }
                     if (eTy != null) {
                       newArr = ArrayDesc.FindArrayType(1,eTy,true); 
                     } else {
                       newArr = new ArrayDesc(elemOrd); 
                     }
                     if ((tNum < typeList.length) && (typeList[tNum] != null)) {
                       PtrDesc desc = (PtrDesc) typeList[tNum];
                       if (desc.inBaseTypeNum != tNum) {
                         System.out.println("WRONG BASE TYPE FOR POINTER!");
                         System.exit(1);
                       }
                       desc.Init(newArr);
                       newArr.SetPtrType(desc);
                     }
                     InsertType(tNum,newArr);
                     break; 
        case ptrSy : TypeDesc ty = null;
                     if (impClass != null) { 
                       InsertType(tNum,impClass);
                       ty = impClass;
                       ty.inTypeNum = tNum;
                       ty.inBaseTypeNum = tOrd;
                       InsertType(tOrd,ty);
                     } else if ((tNum < typeList.length) && 
                                (typeList[tNum] != null) &&
                                (typeList[tNum] instanceof ClassDesc)) { 
                       ty = typeList[tNum];
                       ty.inTypeNum = tNum;
                       ty.inBaseTypeNum = tOrd;
                       InsertType(tOrd,ty);
                     } else {
                       ty = new PtrDesc(tNum,tOrd);
                       InsertType(tNum,ty);
                       if ((tOrd < typeList.length) && 
                                (typeList[tOrd] != null)) { 
                         ((PtrDesc)ty).Init(typeList[tOrd]);
                       }
                     }
                     GetSym();
                     break; 
        case recSy : if ((tNum >= typeList.length) || (typeList[tNum] == null)||
                         (!(typeList[tNum] instanceof ClassDesc))) {
                     /* cannot have record type that is not a base type
                        of a pointer in a java file                     */
                       System.err.println(
                         "RECORD TYPE " + tNum + " IS NOT POINTER BASE TYPE!");
                       System.exit(1);
                     }
                     aClass = (ClassDesc) typeList[tNum];
                     acc = in.readByte();
                     aClass.setRecAtt(acc);
                     if (aClass.read) { 
                       GetSym();
                       SkipToEndRec(in); 
                       GetSym();
                     } else {
                       GetSym();
                       if (sSym == truSy) { 
                         aClass.isInterface = true; 
                         GetSym();
                       } else if (sSym == falSy) { 
                         GetSym(); 
                       }
                       if (sSym == basSy) { 
                         aClass.superNum = tOrd; 
                         GetSym(); 
                       }
                       if (sSym == iFcSy) {
                         GetSym();
                         aClass.intNums = new int[10];
                         aClass.numInts = 0;
                         while (sSym == basSy) {
                           if (aClass.numInts >= aClass.intNums.length) {
                             int tmp[] = new int[aClass.intNums.length*2];
                             System.arraycopy(aClass.intNums, 0, tmp, 0, aClass.intNums.length);
                             aClass.intNums = tmp;
                           }
                           aClass.intNums[aClass.numInts] = tOrd;
                           aClass.numInts++;
                           GetSym();
                         }
                       }
                       while (sSym == namSy) {
                         FieldInfo f = new FieldInfo(aClass,GetAccess(),name,
                                       null,null); 
                         f.typeFixUp = readOrd(); 
                         GetSym();
                         boolean ok = aClass.fieldList.add(f);
                         aClass.scope.put(f.name,f);
                       } 
                       while ((sSym == mthSy) || (sSym == prcSy) ||
                              (sSym == varSy) || (sSym == conSy)) { 
                         switch (sSym) {
                           case mthSy : GetMethod(aClass); break; 
                           case prcSy : GetProc(aClass); break;
                           case varSy : GetVar(aClass); break;
                           case conSy : GetConstant(aClass); break;
                         }
                       }
                       Expect(endRc);
                     }
                     break; 
        case pTpSy : System.out.println("CANNOT HAVE PROC TYPE IN JAVA FILE!"); 
                     break;
        case evtSy :System.out.println("CANNOT HAVE EVENT TYPE IN JAVA FILE!"); 
                     break;
        case eTpSy : System.out.println("CANNOT HAVE ENUM TYPE IN JAVA FILE!"); 
                     break;
        case tDefS : 
        case close : InsertType(tNum,impClass);
                     break;
        default : char ch = (char) sSym; 
                  System.out.println("UNRECOGNISED TYPE!" + sSym + "  " + ch); 
                  System.exit(1);
      }
    }
    Expect(close);
    Check(keySy); 
    fIn.close();
    // do fix ups...
    for (int i = TypeDesc.specT; i <= maxInNum; i++) {
      if ((typeList[i] != null) && (typeList[i] instanceof ClassDesc)) {
        if (!((ClassDesc)typeList[i]).read) {
          aClass = (ClassDesc)typeList[i];
          if (aClass.superNum != 0) {
            aClass.superClass = (ClassDesc)typeList[aClass.superNum];
          }
          aClass.interfaces = new ClassDesc[aClass.numInts];
          for (int j=0; j < aClass.numInts; j++) {
            aClass.interfaces[j] = (ClassDesc) GetFixUpType(aClass.intNums[j]); 
          }
          int size;
          if (aClass.fieldList == null) { 
            size = 0; 
          } else {
            size = aClass.fieldList.size(); 
          }
          aClass.fields = new FieldInfo[size];
          for (int j=0; j < size; j++) {
            aClass.fields[j] = (FieldInfo)aClass.fieldList.get(j);
            aClass.fields[j].type = GetFixUpType(aClass.fields[j].typeFixUp); 
            if (aClass.fields[j].type instanceof ClassDesc) {
              aClass.AddImport((ClassDesc)aClass.fields[j].type);
            }  
          }
          aClass.fieldList = null;
          if (aClass.methodList == null) { size = 0;
          } else { size = aClass.methodList.size(); }
          aClass.methods = new MethodInfo[size];
          for (int k=0; k < size; k++) {
            aClass.methods[k] = (MethodInfo)aClass.methodList.get(k);
            aClass.methods[k].retType = GetFixUpType(
                                        aClass.methods[k].retTypeFixUp); 
            if (aClass.methods[k].retType instanceof ClassDesc) {
              aClass.AddImport((ClassDesc)aClass.methods[k].retType);
            }  
            aClass.methods[k].parTypes = new TypeDesc[
                                         aClass.methods[k].parFixUps.length];
            for (int j=0; j < aClass.methods[k].parFixUps.length; j++) {
              aClass.methods[k].parTypes[j] = GetFixUpType(
                                              aClass.methods[k].parFixUps[j]);
              if (aClass.methods[k].parTypes[j] instanceof ClassDesc) {
                aClass.AddImport((ClassDesc)aClass.methods[k].parTypes[j]);
              }  
            }
          }
          aClass.methodList = null;
          aClass.read = true;
          aClass.done = true;
        }
      } else if ((typeList[i] != null) && (typeList[i] instanceof ArrayDesc)) {
        FixArrayElemType((ArrayDesc)typeList[i]);
      } else if ((typeList[i] != null) && (typeList[i] instanceof PtrDesc)) {
        PtrDesc ptr = (PtrDesc)typeList[i];
        if (ptr.typeOrd == TypeDesc.arrPtr) { 
          ptr.Init(typeList[ptr.inBaseTypeNum]);
        }
      } else if (typeList[i] != null) { 
        System.out.println("Type " + i + " " + typeList[i].name + 
                           " is NOT array or class"); 
        System.exit(0);
      }
    }
  }
}


